#import "SolarSeekMessageReader.h"
#import "MuseekMessage.h"

@implementation SolarSeekMessageReader 
@synthesize stream;
@synthesize delegate;
@synthesize autoOpenStream;

-(void) setStream:(NSInputStream *)i {
  if (stream) {
    [stream close];
    [stream setDelegate:nil];
    [stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
  }
  
  stream = i;
  
  if (autoOpenStream)
    [self open];
}

-(void) open {
  if (stream) {
    [stream setDelegate:self];
    [stream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [stream open];
  }
}  

-(void) disconnect {
  self.stream = nil;
  
  if ([delegate respondsToSelector:@selector(disconnect)])
    [delegate performSelector:@selector(disconnect)];
}

-(NSMutableData *) getDataWithCapacity:(NSUInteger) capacity {
  if (capacity > (2 * 1024 * 1024))
    NSLog(@"Warn : Large message, more than 2MiB, %d", capacity);
  
  if (previousData) {
    [previousData setLength:0];
    return previousData;
  } else {
    NSUInteger realInitialCapacity = capacity;
    if (capacity > 1024 * 1024)
      realInitialCapacity = 1024 * 1024;
    
    previousData = [[NSMutableData alloc] initWithCapacity:realInitialCapacity];
    return previousData;
  }
}

-(void)readMessage {
  if (![stream hasBytesAvailable]) return;
  
  if (currentMessageData == nil) { // new message
    uint32_t size; 
    [stream read:(void *)&size maxLength:sizeof(uint32_t)];
    
    size = CFSwapInt32LittleToHost(size);
    // BUG : size < 2GiB (NSData limit)
    if ((size > 2147483647L) || (size <= 0)) {
      [self disconnect];
      return;
    }
    
    currentMessageData = [self getDataWithCapacity:size];
    messageSize = size;
  } 
  
  NSUInteger offset = [currentMessageData length];
  NSUInteger remainingBytes = messageSize - offset;
  long bytesRead = 0;
  
  if (remainingBytes > 512) remainingBytes = 512;
  
  [currentMessageData setLength:offset + remainingBytes];
  bytesRead = [stream read:(uint8_t *)([currentMessageData bytes] +offset) maxLength:remainingBytes];
  if ((bytesRead >= 0) && (bytesRead != remainingBytes)) {
    NSUInteger i = offset + bytesRead;
    [currentMessageData setLength:i];
  }
  
  if ([currentMessageData length] >= messageSize) {
    MuseekMessage* message = [[MuseekMessage alloc] init];
    [message appendData:currentMessageData];
    
    if ([delegate respondsToSelector:@selector(handleMessage:)]) 
      [delegate performSelector:@selector(handleMessage:) withObject:message];
    
    message = nil;
    currentMessageData = nil;
    
    if ([previousData length] > 4096) {
      previousData = nil;
    }
  } 
  
//  if (stream && [stream hasBytesAvailable] && (currentMessageData != nil))
//    [self performSelector:@selector(readMessage) withObject:nil afterDelay:1.0f];	
}

-(void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
  if ((eventCode == NSStreamEventEndEncountered) || (eventCode == NSStreamEventErrorOccurred)) 
    [self disconnect];
  
  if (eventCode == NSStreamEventHasBytesAvailable) 
    [self readMessage];
}

+(SolarSeekMessageReader *) readerWithStream:(NSInputStream *)stream andDelegate:(id)delegate {
  SolarSeekMessageReader *reader = [[self alloc] init];
  reader.delegate = delegate;
  reader.stream = stream;
  return reader;
}

- (id) init {
  self = [super init];
  if (self != nil) {
    stream = nil;
    delegate = nil;
    autoOpenStream = NO;
    previousData = nil;
  }
  return self;
}


@end

